<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>大数据面试宝典 | Hexo</title>
  <meta name="keywords" content=" interview , bigdata ">
  <meta name="description" content="大数据面试宝典 | Hexo">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta property="og:type" content="website">
<meta property="og:title" content="Tools Hexo">
<meta property="og:url" content="http://huasir.gitee.io/blog/tools/hexo.html">
<meta property="og:site_name" content="Hexo">
<meta property="og:locale" content="zh_CN">
<meta property="article:published_time" content="2020-06-24T14:07:55.000Z">
<meta property="article:modified_time" content="2020-06-24T14:07:55.825Z">
<meta property="article:author" content="New Wang">
<meta name="twitter:card" content="summary">


<link rel="icon" href="/img/avatar.jpg">

<link href="/blog/css/style.css?v=1.1.0" rel="stylesheet">

<link href="/blog/css/hl_theme/darcula.css?v=1.1.0" rel="stylesheet">

<link href="//cdn.jsdelivr.net/npm/animate.css@4.1.0/animate.min.css" rel="stylesheet">

<script src="//cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js"></script>
<script src="/blog/js/titleTip.js?v=1.1.0" ></script>

<script src="//cdn.jsdelivr.net/npm/highlightjs@9.16.2/highlight.pack.min.js"></script>
<script>
    hljs.initHighlightingOnLoad();
</script>

<script src="//cdn.jsdelivr.net/npm/nprogress@0.2.0/nprogress.min.js"></script>



<script src="//cdn.jsdelivr.net/npm/jquery.cookie@1.4.1/jquery.cookie.min.js" ></script>

<script src="/blog/js/iconfont.js?v=1.1.0" ></script>

<meta name="generator" content="Hexo 4.2.1"></head>
<div style="display: none">
  <input class="theme_disqus_on" value="false">
  <input class="theme_preload_comment" value="">
  <input class="theme_blog_path" value="/blog">
  <input id="theme_shortcut" value="true" />
</div>


<body>
<aside class="nav">
    <div class="nav-left">
        <a href="/blog/" class="avatar_target">
    <img class="avatar" src="/blog/img/avatar.jpg" />
</a>
<div class="author">
    <span>New Wang</span>
</div>

<div class="icon">
    
        
        <a title="rss" href="/atom.xml" target="_blank">
            
                <svg class="iconfont-svg" aria-hidden="true">
                    <use xlink:href="#icon-rss"></use>
                </svg>
            
        </a>
        
    
        
        <a title="email" href="mailto:744539648@qq.com" target="_blank">
            
                <svg class="iconfont-svg" aria-hidden="true">
                    <use xlink:href="#icon-email"></use>
                </svg>
            
        </a>
        
    
        
        <a title="qq" href="http://wpa.qq.com/msgrd?v=3&uin=744539648&site=qq&menu=yes" target="_blank">
            
                <svg class="iconfont-svg" aria-hidden="true">
                    <use xlink:href="#icon-qq"></use>
                </svg>
            
        </a>
        
    
</div>




<ul>
    <li><div class="all active" data-rel="全部文章">全部文章<small>(5)</small></div></li>
    
        
            
            <li><div data-rel="Java">Java<small>(4)</small></div>
                
            </li>
            
        
    
        
            
            <li><div data-rel="Tool">Tool<small>(1)</small></div>
                
            </li>
            
        
    
</ul>
<div class="left-bottom">
    <div class="menus">
    
    
    
    
    </div>
    <div><a class="about  hasFriend  site_url"  href="/about">关于</a><a style="width: 50%"  class="friends">友链</a></div>
</div>
<input type="hidden" id="yelog_site_posts_number" value="5">

<div style="display: none">
    <span id="busuanzi_value_site_uv"></span>
    <span id="busuanzi_value_site_pv"></span>
</div>

    </div>
    <div class="nav-right">
        <div class="friends-area">
    <div class="friends-title">
        友情链接
        <i class="iconfont icon-left"></i>
    </div>
    <div class="friends-content">
        <ul>
            
            <li><a target="_blank" href="http://yelog.org/">叶落阁</a></li>
            
        </ul>
    </div>
</div>
        <div class="title-list">
    <div class="right-top">
        <div id="default-panel">
            <i class="iconfont icon-search" data-title="搜索 快捷键 i"></i>
            <div class="right-title">全部文章</div>
            <i class="iconfont icon-file-tree" data-title="切换到大纲视图 快捷键 w"></i>
        </div>
        <div id="search-panel">
            <i class="iconfont icon-left" data-title="返回"></i>
            <input id="local-search-input" />
            <label class="border-line" for="input"></label>
            <i class="iconfont icon-case-sensitive" data-title="大小写敏感"></i>
            <i class="iconfont icon-tag" data-title="标签"></i>
        </div>
        <div id="outline-panel" style="display: none">
            <div class="right-title">大纲</div>
            <i class="iconfont icon-list" data-title="切换到文章列表"></i>
        </div>
    </div>

    <div class="tags-list">
    <input id="tag-search" />
    <div class="tag-wrapper">
        
            <li class="article-tag-list-item">
                <i class="iconfont icon-tag"></i><a>bigdata</a>
            </li>
        
            <li class="article-tag-list-item">
                <i class="iconfont icon-tag"></i><a>Gitee</a>
            </li>
        
            <li class="article-tag-list-item">
                <i class="iconfont icon-tag"></i><a>Hexo</a>
            </li>
        
            <li class="article-tag-list-item">
                <i class="iconfont icon-tag"></i><a>interview</a>
            </li>
        
            <li class="article-tag-list-item">
                <i class="iconfont icon-tag"></i><a>Jvm</a>
            </li>
        
    </div>

</div>

    
    <nav id="title-list-nav">
        
        <a  class="全部文章 Java "
           href="/blog/2020/07/16/BigDateInterview/"
           data-tag="interview,bigdata"
           data-author="" >
            <span class="post-title" title="大数据面试宝典">大数据面试宝典</span>
            <span class="post-date" title="2020-07-16 22:51:06">2020/07/16</span>
        </a>
        
        <a  class="全部文章 Java "
           href="/blog/2020/07/08/JavaInterview/"
           data-tag="interview"
           data-author="" >
            <span class="post-title" title="Java面试宝之基础篇">Java面试宝之基础篇</span>
            <span class="post-date" title="2020-07-08 20:13:11">2020/07/08</span>
        </a>
        
        <a  class="全部文章 Java "
           href="/blog/2020/06/26/jvmLoadClass/"
           data-tag="Jvm"
           data-author="" >
            <span class="post-title" title="JAVA虚拟机的类加载机制（二）">JAVA虚拟机的类加载机制（二）</span>
            <span class="post-date" title="2020-06-26 23:06:18">2020/06/26</span>
        </a>
        
        <a  class="全部文章 Java "
           href="/blog/2020/06/25/JvmLifeCycle/"
           data-tag="Jvm"
           data-author="" >
            <span class="post-title" title="JAVA虚拟机的生命周期（一）">JAVA虚拟机的生命周期（一）</span>
            <span class="post-date" title="2020-06-25 15:13:11">2020/06/25</span>
        </a>
        
        <a  class="全部文章 Tool "
           href="/blog/2020/06/24/hexo/"
           data-tag="Hexo,Gitee"
           data-author="" >
            <span class="post-title" title="Hexo码云个人博客">Hexo码云个人博客</span>
            <span class="post-date" title="2020-06-24 17:16:28">2020/06/24</span>
        </a>
        
        <div id="no-item-tips">

        </div>
    </nav>
    <div id="outline-list">
    </div>
</div>
    </div>
    <div class="hide-list">
        <div class="semicircle" data-title="切换全屏 快捷键 s">
            <div class="brackets first"><</div>
            <div class="brackets">&gt;</div>
        </div>
    </div>
</aside>
<div id="post">
    <div class="pjax">
        <article id="post-BigDateInterview" class="article article-type-post" itemscope itemprop="blogPost">
    
        <h1 class="article-title">大数据面试宝典</h1>
    
    <div class="article-meta">
        
        
        
        <span class="book">
            <i class="iconfont icon-category"></i>
            
            
            <a  data-rel="Java">Java</a>
            
        </span>
        
        
        <span class="tag">
            <i class="iconfont icon-tag"></i>
            
            <a class="color5">interview</a>
            
            <a class="color3">bigdata</a>
            
        </span>
        
    </div>
    <div class="article-meta">
        
            发布时间 : <time class="date" title='最后更新: 2020-07-17 21:44:21'>2020-07-16 22:51</time>
        
    </div>
    <div class="article-meta">
        
        
        <span id="busuanzi_container_page_pv">
            阅读 :<span id="busuanzi_value_page_pv">
                <span class="count-comment">
                    <span class="spinner">
                      <div class="cube1"></div>
                      <div class="cube2"></div>
                    </span>
                </span>
            </span>
        </span>
        
        
    </div>
    
    <div class="toc-ref">
    
        <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#HDFS"><span class="toc-text">HDFS</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Yarn"><span class="toc-text">Yarn</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#MapReduce"><span class="toc-text">MapReduce</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Spark"><span class="toc-text">Spark</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Hbase"><span class="toc-text">Hbase</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Storm"><span class="toc-text">Storm</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#面试遇到的问题"><span class="toc-text">面试遇到的问题</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#参考文件"><span class="toc-text">参考文件</span></a></li></ol>
    
<style>
    .left-col .switch-btn,
    .left-col .switch-area {
        display: none;
    }
    .toc-level-3 i,
    .toc-level-3 ol {
        display: none !important;
    }
</style>
</div>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h2 id="HDFS"><a href="#HDFS" class="headerlink" title="HDFS"></a>HDFS</h2><p>HDFS是Hadoop的核心组件，HDFS上的文件被分成块进行存储，默认块的大小是64M，块是文件存储处理的逻辑单元。HDFS是Master和Slave的结构。分NameNode（Master节点）、SecondaryNameNode（NameNode冷备份H）、DataNode（slave节点）这几个角色</p>
<p><strong>HDFS元素</strong> </p>
<ul>
<li><p>强烈推荐<a href="https://www.cnblogs.com/ttzzyy/p/12323242.html" target="_blank" rel="noopener">大数据篇：HDFS</a></p>
</li>
<li><p>namenode：</p>
<ol>
<li>管理元数据，文件系统中的文件目录结构，并且能跟踪整个集群中的文件</li>
<li>给子节点分配任务</li>
</ol>
</li>
<li><p>datanode：</p>
<ol>
<li>负债存储数据</li>
<li>实时上报心跳给主节点</li>
</ol>
</li>
<li><p>secondnamenode：</p>
<ol>
<li>定时获取editlog，并跟新到fsimage上。一旦它有新的fsiamge产生，它将拷贝回namenode</li>
<li>namenode下次重启时会使用这个fsimage文件，减少启动时间。</li>
</ol>
</li>
</ul>
<p><strong>HDFS写入流程</strong></p>
<ol>
<li>Client向NameNode发起文件写入的请求。</li>
<li>NameNode根据文件大小和文件块配置情况，返回给Client它管理的DataNode的信息。</li>
<li>Client将文件划分为多个block，根据DataNode的地址，按顺序将block写入DataNode块中。</li>
</ol>
<p><strong>HDFS读流程</strong></p>
<ol>
<li>Client向NameNode发起读取文件的请求。</li>
<li>NameNode返回文件存储的DataNode信息。</li>
<li>Client读取文件信息。</li>
</ol>
<p><strong>三副本位置</strong></p>
<ol>
<li>一份在NameNode指定的DateNode上</li>
<li>一个在指定DataNode非同一rack的DataNode上，</li>
<li>一个在指定DataNode同一rack的DataNode上</li>
</ol>
<p><strong>安全模式</strong></p>
<p>系统启动后先进入安全模式，此时系统中的内容不允许修改和删除，直到安全模式结束。安全模式主要是为了启动检查各个DataNode上数据块的安全性</p>
<p><strong>注意</strong></p>
<ol>
<li>NameNode并不会将文件的分块数据持久化存储，这些信息会在HDFS启动时由各个dataNode上报过来。他把这些数据存入内存中。并且会定时对内存中的数据进行快照。</li>
<li>NameNode并不会将文件的分块数据持久化存储，这些信息会在HDFS启动时由各个dataNode上报过来。他把这些数据存入内存中。并且会定时对内存中的数据进行快照。</li>
</ol>
<p><strong>高可用</strong></p>
<p>  QJM（Quorum Journal Manager）是Hadoop专门为Namenode共享存储开发的组件。其集群运行一组Journal Node，每个Journal 节点暴露一个简单的RPC接口，允许Namenode读取和写入数据，数据存放在Journal节点的本地磁盘。当Namenode写入edit log时，它向集群的所有Journal Node发送写入请求，当多数节点回复确认成功写入之后，edit log就认为是成功写入。例如有3个Journal Node，Namenode如果收到来自2个节点的确认消息，则认为写入成功</p>
<p>  而在故障自动转移的处理上，引入了监控Namenode状态的ZookeeperFailController（ZKFC）。ZKFC一般运行在Namenode的宿主机器上，与Zookeeper集群协作完成故障的自动转移</p>
<h2 id="Yarn"><a href="#Yarn" class="headerlink" title="Yarn"></a>Yarn</h2><p> YARN总体上仍然是Master/Slave结构，在整个资源管理框架中，ResourceManager为Master，NodeManager为Slave,ResourceManager负责对各个NodeManager上的资源进行统一管理和调度。当用户提交一个应用程序时，需要提供一个用以跟踪和管理这个程序的ApplicationMaster，它负责向ResourceManager申请资源，并要求NodeManager启动可以占用一定资源的任务。</p>
<p><strong>YARN元素</strong></p>
<ul>
<li><p>ResourceManager(RM)</p>
<ol>
<li>全局资源管理器，负责整个系统的资源管理和分配。</li>
<li>由两个组件组成.调度器 Scheduler和应用管理器（ApplicationsManager，ASM）</li>
<li>Scheduler根据容量等奖系统中的资源分配给各个正在运行的应</li>
<li>ASM主要负责接收作业，协商获取第一个容器用于执行AM和提供重启失败AM container的服务</li>
</ol>
</li>
<li><p>NodeManager</p>
<ol>
<li>每个节点上的资源和任务管理器</li>
<li>定时向RM汇报本节上的资源使用情况和各个Container的运行状态</li>
<li>接受并处理来自AM的Container启动/停止等各种请求</li>
</ol>
</li>
<li><p>ApplicationMaster</p>
<ol>
<li>用户提交每个应用程序均包含一个AM</li>
<li>主要功能与RM调度器协商以获取资源，进一步分配给内部的任务</li>
<li>与NM通信启动/停止任务，监控任务的运行状态</li>
</ol>
</li>
</ul>
<p><strong>一个应用程序在YARN上的运行步骤</strong></p>
<ol>
<li>用户将应用程序提交到ResourceManager上</li>
<li>ResourceManager为应用程序ApplicationMaster申请资源，并与某个NodeManager通信，以启动ApplicationMaster</li>
<li>ApplicationMaster与ResourceManager通信，为内部要执行的任务申请资源，一旦得到资源后，将于NodeManager通信，以启动对应的任务</li>
<li>所有任务运行完成后，ApplicationMaster向ResourceManager注销，整个应用程序运行结束</li>
</ol>
<p><strong>Contanier</strong></p>
<ol>
<li>是YARN中资源的抽象，封装了某个节点上的多维度资源，如内存、CPU、磁盘、网络等</li>
<li>Container是YARN中资源的抽象，它封装了某个节点上一定量的资源（CPU和内存两类资源）</li>
<li>Container由ApplicationMaster向ResourceManager申请的，由ResouceManager中的资源调度器异步分配给ApplicationMaster</li>
<li>Container的运行是由ApplicationMaster向资源所在的NodeManager发起的，Container运行时需提供内部执行的任务命令（可以使任何命令，比如java、Python、C++进程启动命令均可）以及该命令执行所需的环境变量和外部资源（比如词典文件、可执行文件、jar包等）</li>
<li>Container分为两大类<ul>
<li>运行ApplicationMaster的Container</li>
<li>运行各类任务的Container</li>
</ul>
</li>
</ol>
<p><strong>MRv2运行流程</strong>  </p>
<ol>
<li>MR JobClient向resourceManager(AsM)提交一个job</li>
<li>M向Scheduler请求一个供MR AM运行的container，然后启动它</li>
<li>MR AM启动起来后向AsM注册</li>
<li>MR JobClient向AsM获取到MR AM相关的信息，然后直接与MR AM进行通信</li>
<li>MR AM计算splits并为所有的map构造资源请求</li>
<li>MR AM做一些必要的MR OutputCommitter的准备工作</li>
<li>MR AM向RM(Scheduler)发起资源请求，得到一组供map/reduce task运行的container，然后与NM一起对每一个container执行一些必要的任务，包括资源本地化等</li>
<li>MR AM 监视运行着的task 直到完成，当task失败时，申请新的container运行失败的task</li>
<li>当每个map/reduce task完成后，MR AM运行MR OutputCommitter的cleanup 代码，也就是进行一些收尾工作</li>
<li>当所有的map/reduce完成后，MR AM运行OutputCommitter的必要的job commit或者abort APIs</li>
<li>MR AM退出。</li>
</ol>
<h2 id="MapReduce"><a href="#MapReduce" class="headerlink" title="MapReduce"></a>MapReduce</h2><p>一个block块就会有一个Maptask进行处理. 处理完成后通过Shuffle阶段, 经过一系列分区, 排序, 规约, 分组, 进入reduce端, reduce端经过处理后, output输出处理结果即可. 这就是MapReduce的一个大致工作流程</p>
<p><strong>Map阶段</strong></p>
<ol>
<li>通过FileInputFormat读取文件, (split)解析文件成为key, value对, 输出到第二步.</li>
<li>自定义Map逻辑, 处理key1, value1, 将其转换为key2, value2, 输出到第三步.<br><strong>Shuffle阶段</strong></li>
<li>对key2, value2进行分区.</li>
<li>对不同分区内的数据按照相同的key进行排序.</li>
<li>分组后的数据进行规约(combine操作)，降低数据的网络拷贝（可选步骤）</li>
<li>对排序后的数据, 将相同的key的value数据放入一个集合中, 作为value2.<br><strong>Reduce阶段</strong></li>
<li>对多个map的任务进行合并, 排序. 自定义reduce逻辑, 处理key2, value2, 将其转换为key3, value3, 进行输出.</li>
<li>通过FileOutputFormat输出处理后的数据, 保存到文件.</li>
</ol>
<p><strong>MapTask运行机制详解</strong></p>
<ul>
<li>首先，读取数据组件InputFormat（默认TextInputFormat）会通过getSplits方法对输入目录中文件进行逻辑切片规划得到splits，有多少个split就对应启动多少个MapTask。split与block的对应关系默认是一对一</li>
<li>将输入文件切分为splits之后，由RecordReader对象（默认LineRecordReader）进行读取，以\n作为分隔符，读取一行数据，返回&lt;key，value&gt;。Key表示每行首字符偏移值，value表示这一行文本内容</li>
<li>读取split返回&lt;key,value&gt;，进入用户自己继承的Mapper类中，执行用户重写的map函数。RecordReader读取一行这里调用一次</li>
<li>map逻辑完之后，将map的每条结果通过context.write进行collect数据收集。在collect中，会先对其进行分区处理，默认使用HashPartitioner</li>
<li>接下来，会将数据写入内存，内存中这片区域叫做环形缓冲区，缓冲区的作用是批量收集map结果，减少磁盘IO的影响。我们的key/value对以及Partition的结果都会被写入缓冲区。当然写入之前，key与value值都会被序列化成字节数组</li>
<li>当溢写线程启动后，需要对这80MB空间内的key做排序(Sort)。排序是MapReduce模型默认的行为，这里的排序也是对序列化的字节做的排序。如果job设置过Combiner，那么现在就是使用Combiner的时候了。将有相同key的key/value对的value加起来，减少溢写到磁盘的数据量。Combiner会优化MapReduce的中间结果，所以它在整个模型中会多次使用</li>
<li>合并溢写文件：Ø每次溢写会在磁盘上生成一个临时文件（写之前判断是否有combiner），如果map的输出结果真的很大，有多次这样的溢写发生，磁盘上相应的就会有多个临时文件存在。当整个数据处理结束之后开始对磁盘中的临时文件进行merge合并，因为最终的文件只有一个，写入磁盘，并且为这个文件提供了一个索引文件，以记录每个reduce对应数据的偏移量</li>
</ul>
<p><strong>ReduceTask运行机制详解</strong><br>简要概述: Reduce大致分为copy、sort、reduce三个阶段，重点在前两个阶段。reduceTask会启动Fetcher线程去Copy属于自己的的文件, 首先将文件放入内存缓冲区, 当copy来的文件到达一定阈值, 就会合并文件到磁盘, 然后在磁盘中生成了众多的溢写文件。直到没有map端的数据时才结束, 然后合并磁盘中的文件, 生成最终的文件.</p>
<ul>
<li>Copy阶段，简单地拉取数据。Reduce进程启动一些数据copy线程(Fetcher)，通过HTTP方式请求maptask获取属于自己的文件</li>
<li>Merge阶段。这里的merge如map端的merge动作，只是数组中存放的是不同map端copy来的数值。Copy过来的数据会先放入内存缓冲区中，这里的缓冲区大小要比map端的更为灵活。merge有三种形式：内存到内存；内存到磁盘；磁盘到磁盘。默认情况下第一种形式不启用。当内存中的数据量到达一定阈值，就启动内存到磁盘的merge。与map 端类似，这也是溢写的过程，这个过程中如果你设置有Combiner，也是会启用的，然后在磁盘中生成了众多的溢写文件。第二种merge方式一直在运行，直到没有map端的数据时才结束，然后启动第三种磁盘到磁盘的merge方式生成最终的文件</li>
<li>合并排序。把分散的数据合并成一个大的数据后，还会再对合并后的数据排序</li>
<li>对排序后的键值对调用reduce方法，键相等的键值对调用一次reduce方法，每次调用会产生零个或者多个键值对，最后把这些输出的键值对写入到HDFS文件中</li>
</ul>
<p><strong>设置优化</strong></p>
<p>Shuffle中的缓冲区大小会影响到mapreduce程序的执行效率，原则上说，缓冲区越大，磁盘io的次数越少，执行速度就越快。缓冲区的大小可以通过参数调整, 参数：mapreduce.task.io.sort.mb 默认100M</p>
<h2 id="Spark"><a href="#Spark" class="headerlink" title="Spark"></a>Spark</h2><p><strong>Spark Vs Hadoop</strong></p>
<ol>
<li>Hadoop一个作业称为一个Job，Job里面分为Map Task和Reduce Task阶段，每个Task都在自己的进程中运行，当Task结束时，进程也会随之结束</li>
<li>一个Application -&gt; 多个job -&gt;一个job多个stage -&gt; 一个stage多个task</li>
<li>Spark基于RDD，数据并不存放在RDD中，只是通过RDD进行转换，通过装饰者设计模式，数据之间形成血缘关系和类型转换；</li>
<li>Spark用scala语言编写，相比java语言编写的Hadoop程序更加简洁；</li>
<li>相比Hadoop中对于数据计算只提供了Map和Reduce两个操作，Spark提供了丰富的算子，可以通过RDD转换算子和RDD行动算子，实现很多复杂算法操作，这些在复杂的算法在Hadoop中需要自己编写，而在Spark中直接通过scala语言封装好了，直接用就ok；</li>
<li>Hadoop中对于数据的计算，一个Job只有一个Map和Reduce阶段，对于复杂的计算，需要使用多次MR，这样涉及到落盘和磁盘IO，效率不高；而在Spark中，一个Job可以包含多个RDD的转换算子，在调度时可以生成多个Stage，实现更复杂的功能；</li>
<li>Hadoop中中间结果存放在HDFS中，每次MR都需要刷写-调用，而Spark中间结果存放优先存放在内存中，内存不够再存放在磁盘中，不放入HDFS，避免了大量的IO和刷写读取操作；</li>
<li>Hadoop适合处理静态数据，对于迭代式流式数据的处理能力差；Spark通过在内存中缓存处理的数据，提高了处理流式数据和迭代式数据的性能；</li>
</ol>
<p><strong>Spark体系</strong></p>
<ol>
<li>Spark SQL：提供了类 SQL 的查询,返回 Spark-DataFrame 的数据结构(类似 Hive)</li>
<li>Spark Streaming：流式计算,主要用于处理线上实时时序数据(类似 storm)</li>
<li>MLlib： 提供机器学习的各种模型和调优</li>
<li>GraphX： 提供基于图的算法,如 PageRank</li>
</ol>
<p><strong>体系架构</strong></p>
<p><img src="https://blog-10039692.file.myqcloud.com/1493709487351_2754_1493709487748.png" alt="spark"></p>
<ul>
<li>Spark Core：包含Spark的基本功能；尤其是定义RDD的API、操作以及这两者上的动作。其他Spark的库都是构建在RDD和Spark Core之上的</li>
<li>Spark SQL：提供通过Apache Hive的SQL变体Hive查询语言（HiveQL）与Spark进行交互的API。每个数据库表被当做一个RDD.</li>
<li>Spark Streaming：基于SparkCore实现的可扩展、高吞吐、高可靠性的实时数据流处理。支持从Kafka、Flume等数据源处理后存储到HDFS、DataBase、Dashboard中。对实时数据流进行处理和控制。Spark Streaming允许程序能够像普通RDD一样处理实时数据.</li>
</ul>
<p><strong>RDD特点</strong></p>
<ul>
<li>一种元素集合，包含了数据。它是被分区的，分为多个分区，每个分区分布在集群中的不同节点上，从而让RDD中的数据可以被并行操作</li>
<li>RDD通常通过Hadoop上的文件，即HDFS文件或者Hive表，来进行创建；有时也可以通过应用程序中的集合来创建 </li>
<li>RDD最重要的特性就是，提供了容错性，可以自动从节点失败中恢复过来。即如果某个节点上的RDDpartition，因为节点故障，导致数据丢了，那么RDD会自动通过自己的数据来源重新计算该partition。这一切对使用者是透明的</li>
<li>RDD的数据默认情况下存放在内存中的，但是在内存资源不足时，Spark会自动将RDD数据写入磁盘。（弹性）</li>
</ul>
<p><strong>相关术语</strong></p>
<ol>
<li>Application： 基于Spark的用户程序，包含了一个Driver Program 和集群中多个的Executor</li>
<li>Driver：运行Application的main()函数并且创建SparkContext;</li>
<li>Executor： 是为某Application运行在Worker Node上的一个进程，该进程负责运行Task，并且负责将数据存在内存或者磁盘上，每个Application都有各自独立的Executors;</li>
<li>Cluster Manager： 在集群上获取资源的外部服务(例如：Local、Standalone、Mesos或Yarn等集群管理系统)</li>
<li>Operation： 作用于RDD的各种操作分为Transformation和Action</li>
</ol>
<p>整个 Spark 集群中,分为 Master 节点与 worker 节点,,其中 Master 节点上常驻 Master 守护进程和 Driver 进程, Master 负责将串行任务变成可并行执行的任务集Tasks, 同时还负责出错问题处理等,而 Worker 节点上常驻 Worker 守护进程, Master 节点与 Worker 节点分工不同, Master 负载管理全部的 Worker 节点,而 Worker 节点负责执行任务.<br>Driver 的功能是创建 SparkContext, 负责执行用户写的 Application 的 main 函数进程,Application 就是用户写的程序</p>
<p><strong>Spark的架构设计</strong></p>
<p><img src="https://img-blog.csdn.net/20180905234827863?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzE2NjgxMTY5/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70" alt="spark"></p>
<ol>
<li>Cluster Manager在standalone模式中即为Master主节点，控制整个集群，监控worker。在YARN模式中为资源管理器负责分配资源，有点像YARN中ResourceManager那个角色，大管家握有所有的干活的资源，属于乙方的总包</li>
<li>WorkerNode是可以干活的节点，听大管家ClusterManager差遣，是真正有资源干活的主。从节点，负责控制计算节点，启动Executor或者Driver</li>
<li>Executor是在WorkerNode上起的一个进程，相当于一个包工头，负责准备Task环境和执行</li>
<li>Task，负责内存和磁盘的使用。Task是施工项目里的每一个具体的任务。</li>
<li>Driver是统管Task的产生与发送给Executor的，运行Application 的main()函数，是甲方的司令员。</li>
<li>SparkContext是与ClusterManager打交道的，负责给钱申请资源的，是甲方的接口人。</li>
</ol>
<p><strong>运行流程</strong></p>
<ol>
<li>SparkContext是与ClusterManager打交道的，负责给钱申请资源的，是甲方的接口人。</li>
<li>SparkContext向资源管理器（可以是Standalone，Mesos，Yarn）申请运行Executor资源，并启动StandaloneExecutorbackend</li>
<li>Executor向SparkContext申请Task</li>
<li>SparkContext将应用程序分发给Executor</li>
<li>SparkContext构建成DAG图，将DAG图分解成Stage、将Taskset发送给Task Scheduler，最后由Task Scheduler将Task发送给Executor运行</li>
<li>Task在Executor上运行，运行完释放所有资源</li>
</ol>
<p><strong>宽依赖窄依赖</strong></p>
<p><img src="https://upload-images.jianshu.io/upload_images/1935267-8486023be40f27d3.jpg" alt="spark的宽依赖和窄依赖"></p>
<p>如图所示一个空心方框表示一个RDD，实心蓝底的框表示partition;宽依赖与窄依赖的区分主要是父partition与子partition的对应关系</p>
<ul>
<li><p>窄依赖：父partition对子partition是一对一或多对一（只有一个儿子），窄依赖一般是对RDD进行map，filter，union等Transformations</p>
</li>
<li><p>款依赖：父partition对子partition是一对多(有多个儿子)，宽依赖一般是对RDD进行groupByKey，reduceByKey等操作，就是对RDD中的partition中的数据进行重分区（shffle）</p>
</li>
<li><p>join操作即可能是宽依赖也可能是窄依赖</p>
</li>
<li><p>当执行算子有shffle操作的时候，就划分一个Stage。（即宽依赖来划分Stage）</p>
</li>
</ul>
<p><strong>transformation</strong></p>
<ul>
<li>map： 将RDD中的每个元素传人自定义函数，获取一个新的元素，然后用新的元素组成新的RDD。</li>
<li>filter： 对RDD中每个元素进行判断，如果返回true则保留，返回false则剔除。</li>
<li>flatMap： 与map类似，但是对每个元素都可以返回一个或多个元素。</li>
<li>groupByKey： 根据key进行分组，每个key对应一个Iterable<value>。</li>
<li>reduceByKey：对每个key对应的value进行reduce操作。</li>
<li>sortByKey：对每个key对应的value进行排序操作。</li>
<li>join：对两个包含&lt;key,value&gt;对的RDD进行join操作，每个keyjoin上的pair，都会传入自定义函数进行处理。</li>
<li>cogroup：同join，但是每个key对应的Iterable<value>都会传入自定义函数进行处理。</li>
</ul>
<p><strong>action</strong></p>
<p>action操作主要对RDD进行最后的操作，比如遍历，reduce，保存到文件等，并可以返回结果给Driver程序。action操作执行，会触发一个spark job的运行，从而触发这个action之前所有的transformation的执行，这是action的特性</p>
<ul>
<li>reduce：将RDD中的所有元素进行聚合操作</li>
<li>collect： 将RDD中所有元素获取到本地客户端</li>
<li>count： 获取RDD元素总数</li>
<li>take（n）：获取RDD中前n个元素</li>
<li>saveAsTextFile： 将RDD元素保存到文件中，对每个元素调用toString方法</li>
<li>countByKey： 对每个key对应的值进行count计数</li>
<li>foreach： 遍历RDD中的每个元素</li>
</ul>
<p><strong>RDD持久化</strong><br>要持久化一个RDD，只要调用其cache()或者persist()方法即可。在该RDD第一次被计算出来时，就会直接缓存在每个节点中。但是cache()或者persist()的使用是有规则的，必须在transformation或者textFile等创建了一个RDD之后，直接连续调用cache()或persist()才可以</p>
<p><strong>共享变量</strong></p>
<ul>
<li>BroadcastVariable会将使用到的变量，仅仅为每个节点拷贝一份，更大的用处是优化性能，减少网络传输以及内存消耗。广播变量是只读的</li>
<li>Accumulator则可以让多个task共同操作一份变量，主要可以进行累加操作。task只能对Accumulator进行累加操作，不能读取它的值。只有Driver程序可以读取Accumulator的值</li>
</ul>
<h2 id="Hbase"><a href="#Hbase" class="headerlink" title="Hbase"></a>Hbase</h2><p><strong>数据模型</strong></p>
<ul>
<li>RowKey： 用于唯一标识Hbase中的一条数据（记录），不可重复，按照字典顺序排序，RowKey要简短</li>
<li>TimeStamp： HBase自动赋值时间戳(也可自己实现)，作为版本号，64位整型。Hbase每个cell存储单元（k:v）对同一份数据有多个版本，通过实践戳来区分版本之间的差异，按时间倒叙排序</li>
<li>ColumnFamily：ColumnFamily</li>
<li>Column： 列，对应key</li>
<li>Value： 数据</li>
<li>Cell： 是未解析的字节数组。由{rowkey, column(=<family>+<qualifier>),version}唯一确定的单元。由rowkey，列族，列，版本唯一确定一个单元</li>
</ul>
<p><strong>体系架构</strong></p>
<ul>
<li>Master：<ol>
<li>为RegionServer分配Region</li>
<li>负责 RegionServer的负载均衡</li>
<li>发现失效的RegionServer并重新分配到其他RegionServer工作</li>
<li>管理用户对table的增删改查（只负责请求传递）</li>
</ol>
<ul>
<li>RegionServer：</li>
</ul>
<ol>
<li>维护多个region，处理对这些region的IO请求，真正的增删改查操作</li>
<li>水平切分运行中变得过大的region(裂变，尽量等分)</li>
</ol>
<ul>
<li>Region：</li>
</ul>
<ol>
<li>每个region保存表中某段连续的数据(多行)，每个表一开始只有一个region，随着数据量不断增加，region增大</li>
<li>当 达到一个阈值时，region就会被regionServer水平切分成两个新的region</li>
</ol>
-Store：<ol>
<li>一个region由多个store组成</li>
<li>每一个store对应一个CF(列族),</li>
<li>store包含memstore和storeFile</li>
</ol>
<ul>
<li>MemStore：</li>
</ul>
<ol>
<li>内存缓存区,数据的写操作会先写到memstore中</li>
<li>当memstore中的数增长到一个阈值后，regionServer会启动flashcatch进程写入到storeFile中，每次写入形成一个单独的storeFile</li>
</ol>
<ul>
<li>StoreFile：</li>
</ul>
<ol>
<li>当storeFile数量增长到一定阈值后，系统会进行合并(minor major compaction)，合并过程会进行版本的合并和删除工作,，形成更大的storeFile</li>
</ol>
<ul>
<li>Hfile：</li>
</ul>
<ol>
<li>StoreFile是同一个东西，只不过是站在HDFS角度称这个文件为Hfile,在Hbase角度就叫StoreFile</li>
</ol>
<ul>
<li>HLog：<br>是一个普通的Hadoop SequenceFile,记录数据以及数据的操作。目的是用来做数据容灾。HLog SequenceFile的Value是HBase的KeyValue对象，对应HFile中的KeyValue</li>
</ul>
</li>
</ul>
<p><strong>Hbase读数据</strong></p>
<ol>
<li>Client访问Zookeeper，从ZK获取-ROOT-表的位置信息，</li>
<li>通过访问-ROOT-表获取.META.表的位置</li>
<li>然后确定数据所在的HRegion位置</li>
<li>Client访问HRegion所在的HRegionServer，通过HRegionServer获取需要查找的数据</li>
<li>Client到HRegion的中去查找数据，首先到MemStore中查找，查到直接返回</li>
<li>查不到就去ClockCache中查找，查到直接返回</li>
<li>再查不到就去StoreFile中读数据，把读到的数据存入BlockCache中再返回Client</li>
</ol>
<p><strong>Hbase写数据</strong></p>
<ol>
<li>Client通过Zookeeper调度获取表的元数据信息</li>
<li>Cilent通过rpc协议与RegionServer交互，通过-ROOT-表与.META.表找到对应的对应的Region</li>
<li>将数据写入HLog日志中，如出现意外可以同通过HLog恢复信息</li>
<li>将数据写入Region的MemStore中，当MemStore达到阈值开始溢写，将其中的数据Flush成一个StoreFile</li>
<li>MemStore不断生成新的StoreFile，当StoreFile的数量到达阈值后会出发Compact合并操作，将多个StoreFile合并成一个StoreFile</li>
<li>.StoreFile文件会不断增大，当达到阈值后会出发Split操作，把当前的Region且分为两个新的Region</li>
</ol>
<p><strong>-ROOT-表和.META.</strong></p>
<ul>
<li>-ROOT-表记录.META.表的位置信息(即元数据信息)</li>
<li>而.META.表记录了用户表Region的位置信息</li>
</ul>
<p><strong>rowkey原则</strong></p>
<ul>
<li>rowkey长度原则</li>
<li>rowkey散列原则</li>
<li>rowkey唯一原则</li>
<li>什么是热点</li>
<li>反转： 反转rowkey的例子以手机号为rowkey</li>
<li>时间戳反转： 一个常见的数据处理问题是快速获取数据的最近版本</li>
</ul>
<h2 id="Storm"><a href="#Storm" class="headerlink" title="Storm"></a>Storm</h2><p> Spark Streaming与Storm的优劣分析</p>
<p>事实上，Spark Streaming绝对谈不上比Storm优秀。这两个框架在实时计算领域中，都很优秀，只是擅长的细分场景并不相同。</p>
<p>Spark Streaming仅仅在吞吐量上比Storm要优秀，而吞吐量这一点，也是历来挺Spark Streaming，贬Storm的人着重强调的。但是问题是，是不是在所有的实时计算场景下，都那么注重吞吐量？不尽然。因此，通过吞吐量说Spark Streaming强于Storm，不靠谱。</p>
<p>事实上，Storm在实时延迟度上，比Spark Streaming就好多了，前者是纯实时，后者是准实时。而且，Storm的事务机制、健壮性 / 容错性、动态调整并行度等特性，都要比Spark Streaming更加优秀。</p>
<p>Spark Streaming，有一点是Storm绝对比不上的，就是：它位于Spark生态技术栈中，因此Spark Streaming可以和Spark Core、Spark SQL无缝整合，也就意味着，我们可以对实时处理出来的中间数据，立即在程序中无缝进行延迟批处理、交互式查询等操作。这个特点大大增强了Spark Streaming的优势和功能</p>
<h2 id="面试遇到的问题"><a href="#面试遇到的问题" class="headerlink" title="面试遇到的问题"></a>面试遇到的问题</h2><ol>
<li>hadoop生态技术栈里的spark、flink、hive等掌握不佳</li>
</ol>
<p>Kafka消息传递语义:</p>
<ul>
<li><p>At most once: 消息传递过程中有可能丢失，丢失的消息也不会重新传递，其实就是保证消息不会重复发送或者重复消费</p>
</li>
<li><p>At least once: 消息在传递的过程中不可能会丢失，丢失的消息会重新传递，其实就是保证消息不会丢失，但是消息有可能重复发送或者重新被消费</p>
</li>
<li><p>Exactly once: 这个是大多数场景需要的语义，其实就是保证消息不会丢失，也不会重复被消费，消息只传递一次</p>
</li>
<li><p>当acks=0:Producer发送消息到发送端的buffer中就直接返回了，至于这个消息有没有真的发送到Broker Server，Producer不关心，即使消息发送失败，上面说的retry机制也不起作用，所以在这种场景下，可能就会丢失消息了。类似UDP</p>
</li>
<li><p>当acks=1：Producer发送的消息一定要存储到对应的分区的Leader副本日志文件中才算消息发送成功，要是失败的话，则会尝试retry。在这种场景下，只有当消息已经存储在Leader副本中，但是消息还没有被Follower副本同步的时候，如果Leader副本所在的broker server挂了，消息才会丢失</p>
</li>
<li><p>当acks=-1或者等于all的时候，Producer发送的消息一定要存储到对应的分区的所有的在ISR列表中的副本日志文件中才算消息发送成功，要是失败的话，则会尝试retry。这种场景下消息就很难丢失了，除非所有的副本所在的Broker Server都挂了<br>备注</p>
</li>
</ul>
<p>Storm acker工作原理：<a href="https://blog.csdn.net/s646575997/article/details/51217918" target="_blank" rel="noopener">Storm记录级容错的基本原理</a></p>
<ul>
<li>在storm的topology中有一个系统级组件，叫做acker。这个acker的任务就是追踪从spout中流出来的每一个message id绑定的若干tuple的处理路径，如果在用户设置的最大超时时间内这些tuple没有被完全处理，那么acker就会告知spout该消息处理失败了，相反则会告知spout该消息处理成功了</li>
<li>在spout中系统会为用户指定的message id生成一个对应的64位整数，作为一个root id。root id会传递给acker及后续的bolt作为该消息单元的唯一标识。同时无论是spout还是bolt每次新生成一个tuple的时候，都会赋予该tuple一个64位的整数的id。Spout发射完某个message id对应的源tuple之后，会告知acker自己发射的root id及生成的那些源tuple的id。而bolt呢，每次接受到一个输入tuple处理完之后，也会告知acker自己处理的输入tuple的id及新生成的那些tuple的id。Acker只需要对这些id做一个简单的异或运算，就能判断出该root id对应的消息单元是否处理完成了</li>
</ul>
<p>Hive内部表和外部表：</p>
<ul>
<li><p>内部表：也即Hive管理的表，Hive内部表的管理包括逻辑以及语法上的，也包含实际物理意义上的，也就是说，创建Hive内部表后，表中的数据实际上是存储在表所在的目录内，由Hive本身来管理，什么意思呢？也就是说，如果你想删除表的话，那么，连同表的物理数据，元数据等会一并删除</p>
</li>
<li><p>外部表：相对于内部表来说，其管理仅仅是在逻辑和语法意义上的，实际的数据并非由Hive本身来管理，而是交给了HDFS。当创建一个外部表的时候，仅仅是指向一个外部目录而已。如果你想删除表，只是删除表的元数据信息，并不会对实际的物理数据进行删除。举个栗子</p>
</li>
</ul>
<p>Hive的分区和分桶分区: </p>
<ul>
<li>分区：分区可以让数据的部分查询变得更快，也就是说，在加载数据的时候可以指定加载某一部分数据，并不是全量的数据</li>
<li>分桶：分桶表通常是在原始数据中加入一些额外的结构，这些结构可以用于高效的查询，例如，基于ID的分桶可以使得用户的查询非常的块</li>
<li>分区表：指的就是将数据按照表中的某一个字段进行统一归类，并存储在表中的不同的位置，也就是说，一个分区就是一类，这一类的数据对应到hdfs存储上就是对应一个目录。当我们需要进行处理的时候，可以通过分区进行过滤，从而只去部分数据，而没必要取全部数据进行过滤，从而提升数据的处理效率。且分区表是可以分层级创建<ol>
<li>静态分区表：我们在创建表的时候，就已经给该表中的数据定义好了数据类型，在进行加载数据的时候，我们已经知道该数据属于什么类型，并且直接加载到该分区内就可以了（注意，这里的分区字段不能包含在表定义字段中，因为在向表中load数据的时候，需要手动指定该字段的值）</li>
<li>动态分区表：谓的动态分区表，其实建表方式跟静态分区表没有区别，最主要的区别是在载入数据的时候，静态分区表我们载入数据之前必须保证该分区存在，并且我么已经明确知道载入的数据的类型，知道要将数据加载到那个分区当中去，而动态分区表，在载入的时候，我们事先并不知道该条数据属于哪一类，而是需要hive自己去判断该数据属于哪一类，并将该条数据加载到对应的目录中去</li>
</ol>
</li>
<li>分桶表：在表或者分区中使用分桶通常有两个原因，一个是为了高效的查询，另一个则是为了高效的抽样。</li>
</ul>
<ol start="2">
<li><p>hadoop支撑超大文件存取的架构原理不了解</p>
<p><a href="https://www.cnblogs.com/ttzzyy/p/12323242.html" target="_blank" rel="noopener">大数据篇：HDFS</a></p>
</li>
<li><p>对于实时计算中遇到的超大流量没有研究怎样进行有效的流量控制和反压设计</p>
<p><a href="https://www.cnblogs.com/felixzh/p/11635063.html" target="_blank" rel="noopener">一文搞懂 Flink 网络流控与反压机制</a></p>
<p>flink1.5之前： taskmanager里面只有一个task，producer先占inputchannel-&gt;localbufferbool申请-&gt;networkbufferpool申请(task共享)-&gt;nettybuffer。如果占用完，TCP 的 Socket 通信有动态反馈的流控机制，会把容量为0的消息反馈给上游发送端，所以上游的 Socket 就不会往下游再发送数据 。上游的buffer用完，就不做跑动了额。<br>1.5的反压问题：多路复用tcp，导致一路阻塞，其它路不能使用。</p>
</li>
</ol>
<p>Credit的反压策略实现原理：</p>
<ul>
<li>反压机制作用于 Flink 的应用层</li>
<li>在 ResultSubPartition 和 InputChannel 这一层引入了反压机制</li>
<li>每次上游 SubTask A.2 给下游 SubTask B.4 发送数据时，会把 Buffer 中的数据和上游 ResultSubPartition 堆积的数据量 Backlog size发给下游，</li>
<li>游会接收上游发来的数据，并向上游反馈目前下游现在的 Credit 值，Credit 值表示目前下游可以接收上游的 Buffer 量</li>
</ul>
<p>Flink 如何在吞吐量和延迟之间做权衡：</p>
<ul>
<li>Flink 天然支持流式处理，即每来一条数据就能处理一条，而不是像 Spark Streaming 一样，完全是微批处理</li>
<li>但是为了提高吞吐量，默认使用的 Flink 并不是每来一条数据就处理一条</li>
<li>SubTask 输出的数据并不是直接输出到下游，而是在 ResultSubPartition 中有一个 Buffer 用来缓存一批数据后</li>
<li>Flush 到 Netty 发送到下游 SubTask</li>
<li>Buffer 变满时；Buffer timeout 时；特殊事件来临时，例如：CheckPoint 的 barrier 来临时</li>
</ul>
<ol start="4">
<li>待业时间较长</li>
<li>对自我要达到的技术高度，要求较低；具体表现问及看什么技术书？</li>
</ol>
<h2 id="参考文件"><a href="#参考文件" class="headerlink" title="参考文件"></a>参考文件</h2><p><a href="https://www.cnblogs.com/cxzdy/p/4943159.html" target="_blank" rel="noopener">Hadoop2的Yarn和MapReduce2相关</a></p>
<p><a href="https://blog.csdn.net/weixin_42582592/article/details/83080900" target="_blank" rel="noopener">Hadoop MapReduce八大步骤以及Yarn工作原理详解</a></p>
<p><a href="https://zhuanlan.zhihu.com/p/34436165" target="_blank" rel="noopener">spark 原理简述</a></p>
<p><a href="https://www.jianshu.com/p/461f86936972" target="_blank" rel="noopener">MapReduce工作流程最详细解释</a></p>
<p><a href="https://blog.csdn.net/qq_16681169/article/details/82432841" target="_blank" rel="noopener">Spark工作原理</a></p>
<p><a href="https://www.cnblogs.com/bonelee/p/12441814.html" target="_blank" rel="noopener">Hive架构和工作原理</a></p>
<p><a href="https://www.cnblogs.com/yaohaitao/p/5703288.html" target="_blank" rel="noopener">spark与storm的对比</a></p>
<p><a href="https://www.cnblogs.com/ttzzyy/p/13152810.html" target="_blank" rel="noopener">一文读懂@数据仓库</a></p>

      
       <hr><span style="font-style: italic;color: gray;"> 转载请注明来源，欢迎对文章中的引用来源进行考证，欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论，也可以邮件至 744539648@qq.com </span>
    </div>
</article>







    




    </div>
    <div class="copyright">
        <p class="footer-entry">
    ©2016-2020 Yelog
</p>
<p class="footer-entry">Built with <a href="https://hexo.io/" target="_blank">Hexo</a> and <a href="https://github.com/yelog/hexo-theme-3-hexo" target="_blank">3-hexo</a> theme</p>

    </div>
    <div class="full-toc">
        <button class="full" data-title="切换全屏 快捷键 s"><span class="min "></span></button>
<a class="" id="rocket" ></a>

    </div>
</div>

</body>
<script src="/blog/js/jquery.pjax.js?v=1.1.0" ></script>

<script src="/blog/js/script.js?v=1.1.0" ></script>
<script>
    var img_resize = 'default';
    function initArticle() {
        /*渲染对应的表格样式*/
        
            $("#post .pjax table").addClass("green_title");
        

        /*渲染打赏样式*/
        

        /*高亮代码块行号*/
        
        $('pre code').each(function(){
            var lines = $(this).text().trim().split('\n').length, widther='';
            if (lines>99) {
                widther = 'widther'
            }
            var $numbering = $('<ul/>').addClass('pre-numbering ' + widther).attr("unselectable","on");
            $(this).addClass('has-numbering ' + widther)
                    .parent()
                    .append($numbering);
            for(var i=1;i<=lines;i++){
                $numbering.append($('<li/>').text(i));
            }
        });
        

        /*访问数量*/
        
        $.getScript("//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js");
        

        /*代码高亮，行号对齐*/
        $('.pre-numbering').css('line-height',$('.has-numbering').css('line-height'));

        
        
    }

    /*打赏页面隐藏与展示*/
    

</script>

<!--加入行号的高亮代码块样式-->

<style>
    pre{
        position: relative;
        margin-bottom: 24px;
        border-radius: 10px;
        border: 1px solid #e2dede;
        background: #FFF;
        overflow: hidden;
    }
    code.has-numbering{
        margin-left: 30px;
    }
    code.has-numbering.widther{
        margin-left: 35px;
    }
    .pre-numbering{
        margin: 0;
        position: absolute;
        top: 0;
        left: 0;
        width: 20px;
        padding: 0.5em 3px 0.7em 5px;
        border-right: 1px solid #C3CCD0;
        text-align: right;
        color: #AAA;
        background-color: ;
    }
    .pre-numbering.widther {
        width: 35px;
    }
</style>

<!--自定义样式设置-->
<style>
    
    
    .nav {
        width: 542px;
    }
    .nav.fullscreen {
        margin-left: -542px;
    }
    .nav-left {
        width: 120px;
    }
    
    
    @media screen and (max-width: 1468px) {
        .nav {
            width: 492px;
        }
        .nav.fullscreen {
            margin-left: -492px;
        }
        .nav-left {
            width: 100px;
        }
    }
    
    
    @media screen and (max-width: 1024px) {
        .nav {
            width: 492px;
            margin-left: -492px
        }
        .nav.fullscreen {
            margin-left: 0;
        }
    }
    
    @media screen and (max-width: 426px) {
        .nav {
            width: 100%;
        }
        .nav-left {
            width: 100%;
        }
    }
    
    
    .nav-right .title-list nav a .post-title, .nav-right .title-list #local-search-result a .post-title {
        color: #383636;
    }
    
    
    .nav-right .title-list nav a .post-date, .nav-right .title-list #local-search-result a .post-date {
        color: #5e5e5f;
    }
    
    
    .nav-right nav a.hover, #local-search-result a.hover{
        background-color: #e2e0e0;
    }
    
    

    /*列表样式*/
    

    /* 背景图样式 */
    
    


    /*引用块样式*/
    

    /*文章列表背景图*/
    

    
</style>







</html>
